
#include <direct.h>

#define _DIRECTX_BUILD

//----------------------------------------
//	INCLUDE REQUIRED GRAPHICS LIBRARIES
//----------------------------------------
#ifdef _DIRECTX_BUILD

#endif

#include "AppLayer.h"
#include "TEMacros.h"
#include "TraceEngine.h"

#include <fstream>
#include <iostream>

namespace TraceEngine
{
	// Constructor
	AppLayer::AppLayer( void )
	{


	}

	// Initialize the application
	bool AppLayer::initInstance( void )
	{
		// Set the error log
		std::ofstream errFile( "Err.log" );
		std::cout.rdbuf( errFile.rdbuf() );
		std::cout << "Test";

		// Make sure this is the only instance of the game open
		if( !this->isOnlyInstance( L"Blah" ) )
			return false;

		// Make sure user has enough disk space
		if( !this->checkStorage( 129493 ) )
			return false;

		// Make sure user has enough memory (physical and virtual)
		if( !this->checkMemory( 12, 145 ) )
			return false;

		// Get the CPU speed
		DWORD cpuSpeed = this->readCPUSpeed();
		if( cpuSpeed == 0 )
			TE_ERROR( L"Couldn't read CPU speed." );

		



		return true;
	}

	// Make sure this is the only instance of the program
	bool AppLayer::isOnlyInstance( LPCTSTR gameTitle ) const
	{
		// Create mutex with unique name to this game
		HANDLE hand = CreateMutex( nullptr, true, gameTitle );

		// Check if there was an error (already an instance)
		if( GetLastError() != ERROR_SUCCESS )
		{
			HWND hWnd = FindWindow( gameTitle, nullptr );
			if( hWnd )
			{
				// Display error
				TE_ERROR( L"There is already an instance of the game running." );

				// Show the current window
				ShowWindow( hWnd, SW_SHOWNORMAL );
				SetFocus( hWnd );
				SetForegroundWindow( hWnd );
				SetActiveWindow( hWnd );

				return false;
			}
		}
		
		// Return result
		return true;
	}

	// Check to make sure there is the specified free space on the hard disk
	bool AppLayer::checkStorage( const DWORDLONG neededSpace ) const
	{
		// Get the current drive
		const short drive = _getdrive();

		// Get the drive's free space
		struct _diskfree_t diskfree;
		_getdiskfree( drive, &diskfree );

		// Calculate the needed clusters
		unsigned __int64 const neededClusters =
			neededSpace / ( diskfree.sectors_per_cluster * diskfree.bytes_per_sector );

		// Make sure there is enough space
		if( diskfree.avail_clusters < neededClusters )
		{
			TE_ERROR( L"You do not have enough free space on the drive." );
			return false;
		}
		
		return true;
	}

	// Check to make sure there is enough virtual and physical memory
	bool AppLayer::checkMemory( const DWORDLONG physRAMNeeded, const DWORDLONG virtRAMNeeded ) const
	{
		// Get memory information
		MEMORYSTATUSEX status;
		GlobalMemoryStatusEx( &status );

		// Check Physical RAM
		if( status.ullTotalPhys < physRAMNeeded )
		{
			TE_ERROR( L"You do not have enough RAM." );
			return false;
		}

		// Check for Virtual memory
		if( status.ullAvailVirtual < virtRAMNeeded )
		{
			TE_ERROR( L"You do not have enough Virtual Memory space." );
			return false;
		}

		// Quick test to see if it's in one block
		char *buff = TE_NEW char[static_cast<unsigned int>( virtRAMNeeded )];
		if( buff )
			delete [] buff;
		else
		{
			TE_ERROR( L"There is not enough contiguous memory on your drive." );
			return false;
		}

		return true;
	}

	// Check the CPU speed
	DWORD AppLayer::readCPUSpeed( void ) const
	{
		DWORD bufSize = sizeof( DWORD );
		DWORD dwMHz = 0;
		DWORD type = REG_DWORD;
		HKEY hKey;

		// Try to open the registry entry
		long lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
			L"Hardware\\DESCRIPTION\\System\\CentralProcessor\\0",
			0, KEY_READ, &hKey );

		// Read the key
		if( lError == ERROR_SUCCESS )
			RegQueryValueEx( hKey, L"~MHz", nullptr, &type, (LPBYTE)&dwMHz, &bufSize );

		return dwMHz;
	}

		// Initialize the windows api
	void AppLayer::initializeWindows( int& screenWidth, int& screenHeight )
	{
		WNDCLASSEX wc;
		DEVMODE dm;
		int posX, posY;

		// Get external pointer
		// pAppHandle = this;

		// Get the instance of this application
		m_Instance = GetModuleHandle( NULL );

		// Set the application name
		m_AppName = getAppName();

		// Setup windows class
		wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
		wc.lpfnWndProc = WndProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = 0;
		wc.hInstance = m_Instance;
		wc.hIcon = LoadIcon( NULL, IDI_WINLOGO );
		wc.hIconSm = wc.hIcon;
		wc.hCursor = LoadCursor( NULL, IDC_ARROW );
		wc.hbrBackground = static_cast<HBRUSH>( GetStockObject( BLACK_BRUSH ) );
		wc.lpszMenuName = NULL;
		wc.lpszClassName = m_AppName;
		wc.cbSize = sizeof( WNDCLASSEX );

		// Register the window class
		RegisterClassEx( &wc );

		// Determine the resolution
		screenWidth = GetSystemMetrics( SM_CXSCREEN );
		screenHeight = GetSystemMetrics( SM_CYSCREEN );

		/*if( FULLSCREEN )
		{
			memset( &dm, 0, sizeof( DEVMODE ) );
			dm.dmSize = sizeof( DEVMODE );
			dm.dmPelsWidth = static_cast<unsigned long>( screenWidth );
			dm.dmPelsHeight = static_cast<unsigned long>( screenHeight );
			dm.dmBitsPerPel = 32;
			dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

			// Switch to fullscreen
			ChangeDisplaySettings( &dm, CDS_FULLSCREEN );

			// Set the position
			posX = posY = 0;
		}
		else*/
		{
			// Set the window resolution
			screenWidth = 800;
			screenHeight = 600;

			// Put the window in the middle
			posX = ( GetSystemMetrics( SM_CXSCREEN ) - screenWidth ) / 2;
			posY = ( GetSystemMetrics( SM_CYSCREEN ) - screenHeight ) / 2;
		}

		// Create the window
		m_Wnd = CreateWindowEx( WS_EX_APPWINDOW, m_AppName, m_AppName,
			WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
			posX, posY, screenWidth, screenHeight, NULL, NULL, m_Instance, NULL );

		// Display the window
		ShowWindow( m_Wnd, SW_SHOW );
		SetForegroundWindow( m_Wnd );
		SetFocus( m_Wnd );

		// Hide the cursor
		ShowCursor( false );

		return;
	}

	// Global Windows Message Handle function
	LRESULT CALLBACK WndProc( HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam )
	{
		switch( umsg )
		{
			// Check for distruction
			case WM_DESTROY:
				PostQuitMessage( 0 );
				return 0;

			// Check for closing
			case WM_CLOSE:
				PostQuitMessage( 0 );
				return 0;

			default:
				return DefWindowProc( hwnd, umsg, wparam, lparam );
		}
	}
}